package dslab.core;

import java.io.IOException;
import java.io.Writer;

/**
 * interface specifying what a DMAP implementation should offer
 */
public interface DMAPProtocol extends Protocol {
    /*
     * handler functions each pertaining to a respective instruction of
     * the DMAP protocol; refer to the DMAP specification to find out
     * more about the individual directives
     */

    /**
     * processes the 'login' directive
     * @param out writer bound to client connection output stream
     * @param username username part of the request
     * @param password password part of the request
     * @throws IOException thrown when a write operation fails
     */
    void handleLogin(Writer out, String username, String password) throws IOException;

    /**
     * processes the 'list' directive
     * @param out writer bound to client connection output stream
     * @throws IOException thrown when a write operation fails
     */
    void handleList(Writer out) throws IOException;

    /**
     * processes the 'show' directive
     * @param out writer bound to client connection output stream
     * @param id is of message to show
     * @throws IOException thrown when a write operation fails
     */
    void handleShow(Writer out, int id) throws IOException;

    /**
     * processes the 'delete' directive
     * @param out writer bound to client connection output stream
     * @param id is of message to show
     * @throws IOException thrown when a write operation fails
     */
    void handleDelete(Writer out, int id) throws IOException;

    /**
     * processes the 'logout' directive
     * @param out writer bound to client connection output stream
     * @throws IOException thrown when a write operation fails
     */
    void handleLogout(Writer out) throws IOException;

    /**
     * processes the 'quit' directive
     * @param out writer bound to client connection output stream
     * @throws IOException thrown when a write operation fails
     */
    void handleQuit(Writer out) throws IOException;

    /**
     * General request handling function that multiplexes a request to its
     * associated handler methods.
     *
     * @param out Writer of the connection to the client
     * @param request request string sent by the client
     * @throws IOException in case of a broken connection or writer an exception is thrown
     */
    default void processRequest(Writer out, String request) throws IOException, InterruptedException {
        // TODO: deduplicate code with DMTPProtocol
        String header;
        String body;

        // check if IO is available
        if (out == null)
            throw new IOException();

        if (request.contains(" ")) {
            // split multi word instructions
            var headerAndBody = request.split(" ", 2);
            assert headerAndBody.length == 2;
            header = headerAndBody[0];
            body = headerAndBody[1];
        } else {
            // single word instructions have no body
            header = request;
            body = null;
        }

        // calls respective handlers and optionally does validation (to fail early)
        switch (header) {
            case "login":
                if (body == null || !body.contains(" "))
                    raiseWarning(out, String.format("error illegal instruction body%n"));
                else {
                    var userAndPass = body.split(" ", 2);
                    assert userAndPass.length == 2;
                    handleLogin(out, userAndPass[0], userAndPass[1]);
                }
                break;
            case "list":
                handleList(out);
                break;
            case "show":
                try {
                    // if body is null, NumberFormatException will be thrown
                    handleShow(out, Integer.parseInt(body));
                } catch (NumberFormatException exception) {
                    raiseWarning(out, String.format("error illegal instruction body%n"));
                }
                break;
            case "delete":
                try {
                    // if body is null, NumberFormatException will be thrown
                    handleDelete(out, Integer.parseInt(body));
                } catch (NumberFormatException exception) {
                    raiseWarning(out, String.format("error illegal instruction body%n"));
                }
                break;
            case "logout":
                handleLogout(out);
                break;
            case "quit":
                handleQuit(out);
                break;
            default:
                raiseError(out, String.format("error protocol error%n"));
                break;
        }
    }
}
